using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;

using Atomic.Libraries;
using Atomic.Phonons;

namespace Atomic.Thermodynamics.QuasiHarmonicModels
{
	/*[Serializable]
	public class PhonopyQuasiHarmonicModel
	{
		private PhonopyThermalExpansion thermalExpansion;

		public PhonopyQuasiHarmonicModel(params QuasiHarmonicModelVolumeEntry[] volumeEntries)
			: this((IEnumerable<QuasiHarmonicModelVolumeEntry>)volumeEntries)
		{
		}

		public PhonopyQuasiHarmonicModel(IEnumerable<QuasiHarmonicModelVolumeEntry> volumeEntries)
		{
			VolumeEntries = new ReadOnlyList<QuasiHarmonicModelVolumeEntry>(volumeEntries, (e1, e2) => e1.Volume.CompareTo(e2.Volume));
		}

		/// <summary>
		/// Read input to model in Phonopy format. This corresponds to the format in the Al-QHA sample.
		/// </summary>
		public static PhonopyQuasiHarmonicModel Read(DirectoryInfo directory)
		{
			List<QuasiHarmonicModelVolumeEntry> entries = new List<QuasiHarmonicModelVolumeEntry>();

			string[] ev = File.ReadAllLines(directory.AddFile("e-v.dat").FullName);
			int n = (ev.Length - 1) / 2;

			for (int i = -n; i <= n; i++)
			{
				string s = ev[n + i];
				while (s.Contains("  "))
				{
					s = s.Replace("  ", " ");
				}

				double v = double.Parse(s.Split(' ')[0], CultureInfo.InvariantCulture);
				double e = double.Parse(s.Split(' ')[1], CultureInfo.InvariantCulture);

				HarmonicModelThermalProperties p = ReadThermalProperties(directory.AddFile("thermal_properties.yaml-" + i.ToString()));

				entries.Add(new QuasiHarmonicModelVolumeEntry(v, e, p));
			}

			return new PhonopyQuasiHarmonicModel(entries);
		}

		public void Write(DirectoryInfo directory)
		{
			using (StreamWriter writer = new StreamWriter(directory.AddFile("e-v.dat").FullName))
			{
				int n = (VolumeEntries.Count - 1) / 2;
				int i = -n;
				foreach (QuasiHarmonicModelVolumeEntry entry in VolumeEntries)
				{
					writer.WriteLine("{0:f6} {1:f6}", entry.Volume, entry.Energy);
					WriteThermalProperties(entry.ThermalProperties, directory.AddFile("thermal_properties.yaml-" + i.ToString()));
					i++;
				}

				writer.Close();
			}
		}

		public void Generate()
		{
			int n = (VolumeEntries.Count - 1) / 2;

			List<string> files = new List<string>();
			for (int i = -n; i <= n; i++)
			{
				files.Add("thermal_properties.yaml-" + i.ToString());
			}

			using (TempDirectory temp = new TempDirectory())
			{
				Write(temp.Directory);

				Process p = new Process();
				p.StartInfo.FileName = "phonopy-qha";
				p.StartInfo.Arguments = string.Format("e-v.dat {0} --sparse=50", string.Join(" ", files));
				p.StartInfo.WorkingDirectory = temp.Directory.FullName;
				p.StartInfo.UseShellExecute = false;
				p.StartInfo.RedirectStandardOutput = true;
				p.StartInfo.RedirectStandardError = true;
				p.Start();
				p.WaitForExit();

				if (p.ExitCode != 0)
				{
					throw new Exception(string.Format("Running phonopy-qha failed: {0}", p.StandardError.ReadToEnd()));
				}

				thermalExpansion = ReadThermalExpansion(temp.Directory);
			}
		}

		private PhonopyThermalExpansion ReadThermalExpansion(DirectoryInfo directory)
		{
			double l0 = double.NaN;

			List<PhonopyThermalExpansionEntry> entries = new List<PhonopyThermalExpansionEntry>();
			foreach (string line in File.ReadLines(directory.AddFile("volume-temperature.dat").FullName))
			{
				string s = line.Trim();
				while (s.Contains("  "))
				{
					s = s.Replace("  ", " ");
				}

				double t = double.Parse(s.Split(' ')[0], CultureInfo.InvariantCulture);
				double v = double.Parse(s.Split(' ')[1], CultureInfo.InvariantCulture);
					
				entries.Add(new PhonopyThermalExpansionEntry(t, v, double.NaN));

				if (t == 300.0)
				{
					// Reference volume at T=300K.
					l0 = Math.Pow(v, 1.0 / 3.0);
				}
			}

			for (int i = 0; i < entries.Count; i++)
			{
				entries[i] = new PhonopyThermalExpansionEntry(entries[i].Temperature, entries[i].Volume, Math.Pow(entries[i].Volume, 1.0 / 3.0) / l0 - 1.0);
			}

			return new PhonopyThermalExpansion(entries);
		}

		public static HarmonicModelThermalProperties ReadThermalProperties(FileInfo file)
		{
			// 1 kJ/mol = 0.01036410 eV/atom, 1 J/mol = 0.001 kJ/mol
			double conv = 0.01036410;
			double conv2 = 0.001 * conv;

			int atoms = 0;
			double zeroPointEnergy = 0.0;
			double highTemperatureEntropy = 0.0;
			List<ThermalPropertiesEntry> entries = new List<ThermalPropertiesEntry>();

			List<string> lines = new List<string>(File.ReadLines(file.FullName));

			while (lines.Count > 0)
			{
				string line = lines[0];

				if (line.StartsWith("natom:"))
				{
					atoms = int.Parse(line.Split(':')[1].Trim());
				}

				if (line.StartsWith("zero_point_energy:"))
				{
					zeroPointEnergy = conv * double.Parse(line.Split(':')[1].Trim(), CultureInfo.InvariantCulture);
				}

				if (line.StartsWith("high_T_entropy:"))
				{
					highTemperatureEntropy = conv2 * double.Parse(line.Split(':')[1].Trim(), CultureInfo.InvariantCulture);
				}

				if (line.StartsWith("thermal_properties:"))
				{
					// Continue to next loop.
					break;
				}

				lines.RemoveAt(0);
			}

			while (lines.Count > 0)
			{
				string line = lines[0];

				if (line.StartsWith("- temperature:"))
				{
					double t = double.Parse(line.Split(':')[1].Trim(), CultureInfo.InvariantCulture);
					double f = conv * double.Parse(lines[1].Split(':')[1].Trim(), CultureInfo.InvariantCulture);
					double e = conv2 * double.Parse(lines[2].Split(':')[1].Trim(), CultureInfo.InvariantCulture);
					double c = conv2 * double.Parse(lines[3].Split(':')[1].Trim(), CultureInfo.InvariantCulture);

					entries.Add(new ThermalPropertiesEntry(t, f, e, c));

					lines.RemoveRange(0, 3);
				}

				lines.RemoveAt(0);
			}

			return new HarmonicModelThermalProperties(atoms, zeroPointEnergy, highTemperatureEntropy, entries);
		}

		public static void WriteThermalProperties(HarmonicModelThermalProperties thermalProperties, FileInfo file)
		{
			// 1 kJ/mol = 0.01036410 eV/atom, 1 J/mol = 0.001 kJ/mol
			double conv = 0.01036410;
			double conv2 = 0.001 * conv;

			using (StreamWriter writer = new StreamWriter(file.FullName))
			{
				writer.WriteLine("# Thermal properties / unit cell (natom)");
				writer.WriteLine();
				writer.WriteLine("unit:");
				writer.WriteLine("  temperature:   K");
				writer.WriteLine("  free_energy:   kJ/mol");
				writer.WriteLine("  entropy:       J/K/mol");
				writer.WriteLine("  heat_capacity: J/K/mol");
				writer.WriteLine();
				writer.WriteLine("natom: {0}", thermalProperties.AtomsPerPrimitiveCell);
				writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "zero_point_energy: {0:f7}", thermalProperties.ZeroPointEnergy / conv));
				writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "high_T_entropy: {0:f7}", thermalProperties.HighTemperatureEntropy / conv2));
				writer.WriteLine();
				writer.WriteLine("thermal_properties:");

				foreach (ThermalPropertiesEntry entry in thermalProperties.Entries)
				{
					writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "- temperature: {0:f7}", entry.Temperature));
					writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "  free_energy: {0:f7}", entry.FreeEnergy / conv));
					writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "  entropy: {0:f7}", entry.Entropy / conv2));
					writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "  heat_capacity: {0:f7}", entry.HeatCapacity / conv2));
					writer.WriteLine();
				}

				writer.Close();
			}
		}

		public ReadOnlyList<QuasiHarmonicModelVolumeEntry> VolumeEntries
		{
			get;
			private set;
		}

		public PhonopyThermalExpansion ThermalExpansion
		{
			get
			{
				if (thermalExpansion == null)
				{
					Generate();
				}

				return thermalExpansion;
			}
		}
	}*/
}
